package com.openapi.server.codec;

import com.openapi.config.JT808ServerConfig;
import com.openapi.data.JT808PackageData;
import com.openapi.data.Session;
import com.openapi.data.SessionType;
import com.openapi.protocol.decode.AnalyzeHj212;
import com.openapi.service.KafkaService;
import com.openapi.service.LoggerUtil;
import com.openapi.service.SessionManager;
import com.openapi.service.SessionManagerFactory;
import exsun.jt808.data.base.Encryption;
import exsun.jt808.data.base.IdentifierManage;
import exsun.jt808.data.base.MsgDirection;
import exsun.jt808.data.config.CommonMsgTopic;
import exsun.jt808.data.config.jt808.Jt808MsgIdConfig;
import exsun.jt808.data.config.jt808.Jt808MsgIdTopicDic;
import exsun.jt808.data.config.jt808.Jt808ReceiveMsgTopic;
import exsun.jt808.data.kafka_data.KafkaJT808Data;
import exsun.jt808.data.util.parse.BitOperator;
import exsun.jt808.data.util.parse.DecoderUtils;
import exsun.jt808.data.util.parse.HexStrUtil;
import exsun.jt808.data.util.parse.PackageUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Component
public class MsgDecoder extends ByteToMessageDecoder {

	private static final Logger logger = LoggerFactory.getLogger(MsgDecoder.class);


	private static KafkaService kafkaService;
	@Autowired
	public void setKafkaService(KafkaService kafkaService) {
		MsgDecoder.kafkaService = kafkaService;
	}

	private static JT808ServerConfig jt808ServerConfig;
	@Autowired
	public void setJt808ServerConfig(JT808ServerConfig jt808ServerConfig) {
		MsgDecoder.jt808ServerConfig = jt808ServerConfig;
	}


	@Override
	protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
		String str = "";
		byte[] bs = null;
		try {
			if (byteBuf.readableBytes() <= 0)return;
			str = ByteBufUtil.hexDump(byteBuf);
			logger.info("rev：{}" , str);

			bs = new byte[byteBuf.readableBytes()];
			byteBuf.readBytes(bs);

			byte[] identifier =  DecoderUtils.byteCopy(bs,0,4);
			boolean bIdentifierEqual = Arrays.equals(identifier, IdentifierManage.jsalt12FileIdentifier);
			String channelId = Session.buildId(channelHandlerContext.channel());

			//原始完整包数据
			byte[] wholeBuffer = bIdentifierEqual ? bs : getWholeData(bs);

			JT808PackageData pkd = bIdentifierEqual ? bytes2FilePackageData(bs, channelId) : bytes2Jt808PackageData(wholeBuffer, bs, channelId);
			pkd.tpc = Jt808MsgIdTopicDic.getMsgTopic(pkd.getMid());
			pkd.setSessionType(AnalyzeHj212.bHj212Msg(pkd.getMid())? SessionType.HJ_212 :SessionType.JT_808);
			if(!pkd.tpc.equals(Jt808ReceiveMsgTopic.fileStream)){
				sendMessageToMQ(pkd.dvo, pkd.mid, pkd.tpc, wholeBuffer);
			}

			if (pkd == null || (!pkd.getbValid())) return;

			list.add(pkd);
		} catch (Exception e)
		{
			logger.info("decode 异常:{},{}" , HexStrUtil.encodeHex(bs) , e.getMessage());
		}
	}

	/**
	 * 加上7e标志符的完整808数据包
	 * @param bs
	 * @return
	 */
	private byte[] getWholeData(byte[] bs)
	{
		byte[] temp = new byte[bs.length+2];
		System.arraycopy(bs,0,temp,1,bs.length);
		temp[0] = IdentifierManage.jt808Identifier;
		temp[bs.length + 1] = IdentifierManage.jt808Identifier;
		return temp;
	}


	/**
	 * 解析苏标文件协议
	 * @param rec
	 * @param channelId
	 * @return
	 */
	private JT808PackageData bytes2FilePackageData(byte[] rec, String channelId)
	{
		JT808PackageData pak = new JT808PackageData();
		pak.setBodyBuffers(rec);
		pak.setbValid(true);
		SessionManager sessionManager = SessionManagerFactory.GetSessionManager(SessionType.HJ_212);
		Session session = sessionManager.findBySessionId(channelId);
		if(session != null){
			pak.dvo = session.getDevNo();
		}else{
			logger.error("解析苏标文件协议,session为null");
		}
		pak.setMid(Jt808MsgIdConfig.jsalt12FileMsgId);
		pak.setTpc(Jt808MsgIdTopicDic.getMsgTopic(pak.mid));
		pak.setChannelId(channelId);
		return pak;
	}

	/**
	 * 解析808标准协议
	 * @param rec
	 * @return
	 * @throws Exception
	 */
	private JT808PackageData bytes2Jt808PackageData(byte[] wholeBuffer, byte[] rec, String channelId) throws Exception
	{
		byte[] data = PackageUtil.doEscape4Receive(rec, 0, rec.length);

		JT808PackageData pak = parseMsgHeaderFromBytes(data);
		pak.setBuffers(rec);
		pak.setChannelId(channelId);
		//打印原始数据的日志
		writeBufferLog(pak, wholeBuffer);
		if (pak.getVersionSign() == 1 && pak.getVersionId() != null){
			if (data.length < 17){
				logger.info("消息头字节错误：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
				return null;
			}

			int msgBodyByteStartIndex = 17;
			// 2. 消息体
			// 有子包信息,消息体起始字节后移四个字节:消息包总数(word(17))+包序号(word(17))
			if (pak.getSubcontracting())
			{
				msgBodyByteStartIndex = 21;
			}
			if ((msgBodyByteStartIndex + pak.getMessageLength()) > data.length)
			{
				logger.info("消息无效：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
				return pak;
			}

			byte[] msgBodyByte = new byte[pak.getMessageLength()];
			System.arraycopy(data, msgBodyByteStartIndex, msgBodyByte, 0, msgBodyByte.length);
			pak.setBodyBuffers(msgBodyByte);
			// 3. 去掉分隔符之后，最后一位就是校验码
			int checkSumInPkg = data[data.length - 1];
			int calculatedCheckSum = BitOperator.getCheckSum4JT808(data, 0, data.length - 1);
			pak.setCheckSum(checkSumInPkg);
			if (checkSumInPkg != calculatedCheckSum)
			{
				pak.setbValid(false);
				logger.info("检验码不一致：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
			} else
			{
				pak.setbValid(true);
			}
			return pak;
		} else {
			if (data.length < 12){
				logger.info("消息头字节错误：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
				return null;
			}
			int msgBodyByteStartIndex = 12;
			// 2. 消息体
			// 有子包信息,消息体起始字节后移四个字节:消息包总数(word(16))+包序号(word(16))
			if (pak.getSubcontracting())
			{
				msgBodyByteStartIndex = 16;
			}
			if ((msgBodyByteStartIndex + pak.getMessageLength()) > data.length)
			{
				logger.info("消息无效：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
				return pak;
			}

			byte[] msgBodyByte = new byte[pak.getMessageLength()];
			System.arraycopy(data, msgBodyByteStartIndex, msgBodyByte, 0, msgBodyByte.length);
			pak.setBodyBuffers(msgBodyByte);
			// 3. 去掉分隔符之后，最后一位就是校验码
			int checkSumInPkg = data[data.length - 1];
			int calculatedCheckSum = BitOperator.getCheckSum4JT808(data, 0, data.length - 1);
			pak.setCheckSum(checkSumInPkg);
			if (checkSumInPkg != calculatedCheckSum)
			{
				pak.setbValid(false);
				logger.info("检验码不一致：{}, data:{}", HexStrUtil.encodeHex(rec), HexStrUtil.encodeHex(data));
			} else
			{
				pak.setbValid(true);
			}
			return pak;
		}
	}

	/**
	 * 消息头解析
	 * @param data 转义数据
	 * @return
	 */
	private JT808PackageData parseMsgHeaderFromBytes( byte[] data)
	{
		JT808PackageData packageData = new JT808PackageData();

		// 1. 消息ID word(16)
		packageData.setMid(DecoderUtils.parseIntFromBytes(data, 0, 2));

		// 2. 消息体属性 word(16)=================>
		int msgBodyProps = DecoderUtils.parseIntFromBytes(data, 2, 2);
		// [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
		packageData.setMessageLength(msgBodyProps & 0x3ff);
		// [10-12] 0001,1100,0000,0000(1C00)(加密类型)
		packageData.setEncryption(((msgBodyProps & 0x1c00) >> 10) == 0 ? Encryption.None : Encryption.Rsa);
		// [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
		packageData.setSubcontracting(((msgBodyProps & 0x2000) >> 13) == 1);
		// [14] 0100 0000 0000 0000(4000)(版本标识)
		packageData.setVersionSign((msgBodyProps & 0x4000) >> 14);

		//暂时处理 部分设备数据异常问题
		int propBit15 = (msgBodyProps & 0x8000) >> 15;

		if(propBit15 == 0 && packageData.getVersionSign() == 1 ){

			packageData.setMedia(((msgBodyProps & 0x8000) >> 15) == 1);

			// 3. 版本协议号
			packageData.setVersionId(DecoderUtils.parseIntFromBytes(data,4,1));

			try {
				packageData.setDvo(HexStrUtil.encodeHex(data, 5, 10));
			} catch (Exception e) {
				logger.info(" 终端手机号解析异常： " + HexStrUtil.encodeHex(data));
			}
			if (packageData.getDvo() != null && packageData.getDvo().length() > 20) {
				logger.info(" mobile 异常：" + packageData.getDvo() + "," + HexStrUtil.encodeHex(data, 5, 10));
			}
			// 4. 消息流水号 word(15) 按发送顺序从 0 开始循环累加
			if (data.length < 17) {
				logger.info(" 消息流水号异常：" + packageData.getDvo() + "," + HexStrUtil.encodeHex(data));
				packageData.setMsgSerialNo(0);
				return packageData;
			}
			packageData.setMsgSerialNo(DecoderUtils.parseIntFromBytes(data, 15, 2));

			// 5. 消息包封装项
			// 有子包信息
			if (packageData.getSubcontracting()) {
				// byte[0-1] 消息包总数(word(17))
				packageData.setPackageSize(DecoderUtils.parseIntFromBytes(data, 17, 2));
				// byte[2-3] 包序号(word(17)) 从 1 开始
				packageData.setPackageIndex(DecoderUtils.parseIntFromBytes(data, 19, 2));
			} else {
				packageData.setPackageIndex(1);
				packageData.setPackageSize(1);
			}
			return packageData;
		}else{
			packageData.setMedia(((msgBodyProps & 0x8000) >> 15) == 1);
			try {
				packageData.setDvo(HexStrUtil.encodeHex(data, 4, 6));
			} catch (Exception e) {
				logger.info(" 终端手机号解析异常： " + HexStrUtil.encodeHex(data));
			}
			if (packageData.getDvo() != null && packageData.getDvo().length() > 12) {
				logger.info(" mobile 异常：" + packageData.getDvo() + "," + HexStrUtil.encodeHex(data, 4, 6));
			}
			// 4. 消息流水号 word(16) 按发送顺序从 0 开始循环累加
			if (data.length < 12) {
				logger.info(" 消息流水号异常：" + packageData.getDvo() + "," + HexStrUtil.encodeHex(data));
				packageData.setMsgSerialNo(0);
				return packageData;
			}
			packageData.setMsgSerialNo(DecoderUtils.parseIntFromBytes(data, 10, 2));

			// 5. 消息包封装项
			// 有子包信息
			if (packageData.getSubcontracting()) {
				// byte[0-1] 消息包总数(word(16))
				packageData.setPackageSize(DecoderUtils.parseIntFromBytes(data, 12, 2));
				// byte[2-3] 包序号(word(16)) 从 1 开始
				packageData.setPackageIndex(DecoderUtils.parseIntFromBytes(data, 14, 2));
			} else {
				packageData.setPackageIndex(1);
				packageData.setPackageSize(1);
			}
			return packageData;
		}
	}

	/**
	 * 原始报文发送
	 */
	public void sendMessageToMQ(String devNo, int msgId, String topic, byte[] wholeBuffer)
	{
		int port = jt808ServerConfig.getJt808ServerPort();
		KafkaJT808Data kafkaJT808Data = new KafkaJT808Data(wholeBuffer, devNo, msgId, topic, MsgDirection.up, port);
		String sendData =kafkaJT808Data.getJsonFromObject();
		kafkaService.push(CommonMsgTopic.rawData, kafkaJT808Data.getDevNo(), sendData);
	}

	/**
	 * 打印原始数据的日志
	 * @param wholeBuffer
	 */
	private void  writeBufferLog(JT808PackageData pak, byte[] wholeBuffer)throws Exception{
		if(pak.getMid() == Jt808MsgIdConfig.gpsData){
			LoggerUtil.log(pak.dvo, HexStrUtil.encodeHex(wholeBuffer));
		}
	}

}
